#include "scu.h"

int INTMIN(int a, int b)
{
  if (a < b)
    return a;
  else
    return b;
}
            
scu_buf_t scuBufferCreate(int size, uint_fast8_t maxreaders,
               		  buf_destructor_t destructor)
{
  scu_buf_t buf;
  
  buf.data = (uint8_t*) calloc(size, 1);
  buf.references = 1; /* writer */
  buf.readers = 0;
  buf.writer.ptr = 0;
  buf.writer.newptr = 0;
  buf.finalized = 0;
  buf.locked = 1;
  buf.size = size;
  
  return buf;
}

int scuBufferDestroy(scu_buf_t *buf)
{
  free(buf->data);
  return 0;
}

int scuBufferRemoveReference(scu_buf_t *buf)
{
  buf->references--;
  
  if (!buf->references && !buf->locked)
    return scuBufferDestroy(buf);
  
  return 0;
}

int scuBufferFinalize(scu_buf_t *buf)
{
  buf->finalized = 1;
  return scuBufferRemoveReference(buf);
}

int scuBufferIsDry(scu_buf_t *buf, scu_bufptrid_t ptr)
{
  return (buf->finalized &&
          buf->reader[ptr].ptr == buf->writer.ptr);
}

scu_bufptrid_t scuBufferAddReader(scu_buf_t *buf)
{
  buf->reader[buf->readers].ptr = 0;
  buf->reader[buf->readers].newptr = 0;
  buf->references++;

  return buf->readers++;  
}

int scuBufferUnlock(scu_buf_t *buf)
{
  buf->locked = 0;
  
 if (!buf->references)
   return scuBufferDestroy(buf);
  
  return 0;
}

int scuBufferSpaceAvailable(scu_buf_t *buf)
{
  return buf->size - buf->writer.ptr;
}

int scuBufferWrite(scu_buf_t *buf, void *data, int size, int options)
{
  size = INTMIN(scuBufferSpaceAvailable(buf), size);

  memcpy(&buf->data[buf->writer.ptr], data, size);
  buf->writer.ptr += size;
  if (buf->writer.ptr > buf->writer.newptr)
    buf->writer.newptr = buf->writer.ptr;
  
  return size;
}

int scuBufferDataAvailable(scu_buf_t *buf, scu_bufptrid_t r)
{
  return buf->writer.ptr - buf->reader[r].ptr;
}

int scuBufferRead(scu_buf_t *buf, scu_bufptrid_t r, void *data, int size,
                  int options)
{
  size = INTMIN(scuBufferDataAvailable(buf, r), size);

  memcpy(data, &buf->data[buf->reader[r].ptr], size);
  buf->reader[r].ptr += size;
  if (buf->reader[r].ptr > buf->reader[r].newptr)
    buf->reader[r].newptr = buf->reader[r].ptr;
  
  return size;
}

int scuBufferPreSpaceAvailable(scu_buf_t *buf)
{
  return buf->size - buf->writer.ptr;
}  

int  scuBufferAllocate(scu_buf_t *buf, void **data, int size, int options)
{
  size = INTMIN(scuBufferPreSpaceAvailable(buf), size);

  *data = &buf->data[buf->writer.newptr];
  buf->writer.newptr += size;

//  printf("Allocating %d for %d\n", size, buf->writer.newptr);
  
  return size;
}

void scuBufferReleaseAllocated(scu_buf_t *buf, int size)
{
  buf->writer.ptr += size;
}

int scuBufferPreDataAvailable(scu_buf_t *buf, scu_bufptrid_t r)
{
  return buf->writer.ptr - buf->reader[r].newptr;
}   
   
int  scuBufferPeek(scu_buf_t *buf, scu_bufptrid_t r, void **data, int size,
                   int options)
{
  size = INTMIN(scuBufferPreDataAvailable(buf, r), size);

  *data = &buf->data[buf->reader[r].newptr];
  buf->reader[r].newptr += size;
  
//  printf("Peeking %d for %d\n", size, buf->reader[r].newptr);

  return size;
}

void scuBufferReleasePeeked(scu_buf_t *buf, scu_bufptrid_t r, int size)
{
  buf->reader[r].ptr += size;
}
